﻿using System;
using System.Collections.ObjectModel;
using System.IdentityModel.Tokens;
using SecurityTokenServiceNS;
using System.IdentityModel.Claims;
using System.ServiceModel.Security;
using System.ServiceModel.Security.Tokens;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Text;

namespace IssuedClaimsAddIns
{
	public class IssuedClaimsModuleOpenID : IIssuedClaimsProcessor
	{
		static private int _keySize = 256;
		private string[] _signedKeys = { "op_endpoint", "claimed_id", "identity", "return_to", "response_nonce", "assoc_handle" };
		private byte[] _issuerEntropy = new byte[_keySize / 8];

		public IssuedClaimsModuleOpenID()
		{
			// TODO: persist the _issuerEntropy for use by the OpenId Provider
			new RNGCryptoServiceProvider().GetNonZeroBytes(_issuerEntropy);
		}

		public void GetIssuedClaims(RST rst, Collection<Claim> claims)
		{
			if (rst.TokenType == Constants.OpenID.NamespaceUri)
			{
				string[] signedValues = new string[_signedKeys.Length];
				byte[] assocHandle = new byte[_keySize / 8];
				new RNGCryptoServiceProvider().GetNonZeroBytes(assocHandle);

				claims.Add(new Claim(Constants.OpenID.Elements.ns, Constants.OpenID.NamespaceUri, Rights.PossessProperty));
				claims.Add(new Claim(Constants.OpenID.Elements.mode, "id_res", Rights.PossessProperty));
//				claims.Add(new Claim(Constants.OpenID.Elements.op_endpoint, "http://84.90.95.170/OpenIdProvider/", Rights.PossessProperty));
				claims.Add(new Claim(Constants.OpenID.Elements.op_endpoint, "http://neptune/OpenIdProvider/", Rights.PossessProperty));
				claims.Add(new Claim(Constants.OpenID.Elements.claimed_id, GetID(rst), Rights.PossessProperty));
				claims.Add(new Claim(Constants.OpenID.Elements.identity, GetID(rst), Rights.PossessProperty));
//				claims.Add(new Claim(Constants.OpenID.Elements.return_to, rst.AppliesTo.Uri, Rights.PossessProperty));
				claims.Add(new Claim(Constants.OpenID.Elements.return_to, "https://neptune/StsManager/StsManager.aspx", Rights.PossessProperty));
				claims.Add(new Claim(Constants.OpenID.Elements.response_nonce, DateTime.UtcNow.ToString(@"yyyy-MM-ddThh:mm:ssZ"), Rights.PossessProperty));
				claims.Add(new Claim(Constants.OpenID.Elements.assoc_handle, Convert.ToBase64String(assocHandle), Rights.PossessProperty));
				claims.Add(new Claim(Constants.OpenID.Elements.signed, GetListToSign(claims, signedValues), Rights.PossessProperty));
				claims.Add(new Claim(Constants.OpenID.Elements.sig, GetSignature(signedValues, assocHandle), Rights.PossessProperty));
			}
		}
		private string GetID(RST rst)
		{
			// TODO: Obtain id information from data contained in the rst
			return "http://neptune/19070/";
		}
		private string GetListToSign(Collection<Claim> claims, string[] signedValues)
		{
			string list = "";
			for (int i = 0 ; i < _signedKeys.Length ; ++i)
				foreach (Claim claim in claims)
					if (_signedKeys[i] == claim.ClaimType)
					{
						signedValues[i] = claim.Resource.ToString();
						if (list != "")
							list += ",";
						list += _signedKeys[i];
					}
			return list;
		}
		private string GetSignature(string[] signedValues, byte[] assocHandle)
		{
			for (int i = 0; i < _keySize / 8; ++i)
				assocHandle[i] ^= _issuerEntropy[i];

			HMACSHA256 hmacsha256 = new HMACSHA256(assocHandle);

			string list = "";
			for (int i = 0 ; i < signedValues.Length ; ++i)
				list += _signedKeys[i] + ":" + signedValues[i] + "\n";

			return Convert.ToBase64String(hmacsha256.ComputeHash(ASCIIEncoding.ASCII.GetBytes(list)));
		}
	}
}
